package com.leenmvc.core.utils;

import com.leenmvc.core.annotation.*;
import com.leenmvc.core.annotation.validator.Foreign;
import com.leenmvc.core.annotation.validator.Uniqueness;
import com.leenmvc.core.annotation.validator.ValueIn;
import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.dao.communal.Dialect;
import com.leenmvc.core.enums.FillType;
import com.leenmvc.core.exception.BusinessException;
import com.leenmvc.core.utils.collection.ArrayUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 反射工具类
 *
 */
public class ClassUtil {

    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    private static Pattern linePattern = Pattern.compile("(_)(\\w)");

    /**
     * 获取表名
     *
     * @param cls
     * @return
     */
    public static String getTableName(Class<?> cls) {
        DbInfo annotation = cls.getAnnotation(DbInfo.class);
        String tableName;
        if (annotation != null) {
            tableName = annotation.value();
        } else {
            // 类上没有注解，意味着表名和类名一致
            tableName = cls.getSimpleName();
        }
        return tableName;
    }

    /**
     * 获取表名
     *
     * @param cls
     * @return
     */
    public static String getTableComment(Class<?> cls) {
        DbInfo annotation = cls.getAnnotation(DbInfo.class);
        String tableName;
        if (annotation != null) {
            tableName = annotation.comment();
        } else {
            // 类上没有注解，用类名直接替代
            tableName = cls.getSimpleName();
        }
        return tableName;
    }

    /**
     * 获取主键列(只支持一个主键)
     * 复合主键慎用
     *
     * @return
     */
    public static String getPrimaryColumnName(Class<?> cls) {
        Field[] fields = cls.getDeclaredFields();
        for(int i = 0; i < fields.length; i++) {
            PrimaryKey annotation = fields[i].getAnnotation(PrimaryKey.class);
            if(annotation != null) {
                return getColumnName(fields[i]);
            }
        }
        return null;
    }


    /**
     * 获取主键字段名
     *
     * @return
     */
    public static String getPrimaryFieldName(Class<?> cls) {
        Field primaryField = getPrimaryField(cls);
        if(primaryField == null) return null;
        return getPrimaryField(cls).getName();
    }

    /**
     * 获取主键列
     *
     * @return
     */
    public static Field getPrimaryField(Class<?> cls) {
        Field[] fields = cls.getDeclaredFields();
        for(int i = 0; i < fields.length; i++) {
            PrimaryKey annotation = fields[i].getAnnotation(PrimaryKey.class);
            if(annotation != null) {
                return fields[i];
            }
        }
        return null;
    }

    /**
     * 获取主键字段的set方法
     *
     * @param cls
     * @return
     */
    public static Method getPrimarySetMethod(Class<?> cls) {
        Field primaryField = getPrimaryField(cls);
        try {
            return cls.getDeclaredMethod("set" + StringUtils.upperFirstLatter(primaryField.getName()), primaryField.getType());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取主键字段的get方法
     *
     * @param cls
     * @return
     */
    public static Method getPrimaryGetMethod(Class<?> cls) {
        Field primaryField = getPrimaryField(cls);
        try {
            return cls.getDeclaredMethod("get" + StringUtils.upperFirstLatter(primaryField.getName()), null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取列名(驼峰命名)
     *
     * @param field
     * @return
     */
    public static String getColumnName(Field field) {
        DBColumnName annotation = field.getAnnotation(DBColumnName.class);
        String columnName = null;
        if (annotation != null) {
            columnName = annotation.value();
        } else {
            columnName = humpToLine(field.getName());
        }

        return columnName;
    }

    /**
     * 是否为数据库字段
     *
     * @param field
     * @return
     */
    public static boolean isTableField(Field field) {
        TableField annotation = field.getAnnotation(TableField.class);
        String columnName = null;
        if (annotation != null) {
            return annotation.value();
        }
        return true;
    }

    /**
     * 根据 给定字符串获取class
     * @param className
     * @return
     */
    public static Class<?> getClass(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 是否需要填充外表内容到该字段
     *
     * @param field
     * @return
     */
    public static boolean isFillField(Field field) {
        FillField annotation = field.getAnnotation(FillField.class);
        if (annotation != null) {
            return true;
        }
        return false;
    }

    /**
     * 获取数据库字段列表
     *
     * @param cls
     * @return
     */
    public static List<String> getColumns(Class<?> cls) {
        List<String> result  = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation != null && !annotation.value()) {
                continue;
            }
            result.add(cls.getSimpleName() + "." + getColumnName(field));
        }
        return result;
    }

    /**
     * 获取数据库字段列表
     *
     * @param cls
     * @return
     */
    public static String[] getColumnsArr(Class<?> cls) {
        String[] result  = new String[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation != null && !annotation.value()) {
                continue;
            }
            result = ArrayUtils.arrayAdd(result, cls.getSimpleName() + "." + getColumnName(field));
        }
        return result;
    }

    /**
     * 获取数据库字段列表
     *
     * @param cls
     * @return
     */
    public static List<String> getColumnList(Class<?> cls) {
        List<String> result  = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation != null && !annotation.value()) {
                continue;
            }
            result.add(getColumnName(field));
        }
        return result;
    }

    /**
     * 获取数据库字段列表
     *
     * @param cls
     * @return
     */
    public static List<String> getFields(Class<?> cls) {
        List<String> result  = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation != null && !annotation.value()) {
                continue;
            }
            result.add(field.getName());
        }
        return result;
    }

    /**
     * 获取数据库字段列表
     *
     * @param cls
     * @return
     */
    public static String[] getFieldsArr(Class<?> cls) {
        List<String> fields = getFields(cls);
        Field[] fillFieldOneFieldInDefaultQuery = getFillFieldOneFieldInDefaultQuery(cls);
        for(Field field: fillFieldOneFieldInDefaultQuery) {
            fields.add(field.getName());
        };
        return fields.toArray(new String[fields.size()]);
    }



    /** 驼峰转下划线,效率比上面高 */
    public static String humpToLine(Object str) {
        Matcher matcher = humpPattern.matcher(str.toString());
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 下划线转驼峰
     * @param str
     * @return
     */
    public static String lineToHump(String str){
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()){
            matcher.appendReplacement(sb, matcher.group(2).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 根据名字获取字段, 如果子类里面没有那么就去父类li
     *
     * @param cls
     * @param name
     * @return
     */
    public static Field getField(Class<?> cls, String name) {
        try {
            return cls.getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            Class<?> superclass = cls.getSuperclass();
            try {
                return superclass.getDeclaredField(name);
            } catch (NoSuchFieldException e1) {
                e1.printStackTrace();
            }
            throw new BusinessException("在类和其父类中都未找到给定的字段" + cls.getName() + ":" + name);
        }
    }

    /**
     * 判断你一个类是否存在某个属性（字段）
     *
     * @param fieldName 字段
     * @param cls   类对象
     * @return true:存在，false:不存在, null:参数不合法
     */
    public static Boolean isExistField(Class<?> cls, String fieldName) {
        if (cls == null || StringUtils.isEmpty(fieldName)) {
            return false;
        }
        // 获取这个类的所有属性
        Field[] fields = cls.getDeclaredFields();
        // 循环遍历当前类的所有fields
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(fieldName)) {
                return true;
            }
        }

        // 循环遍历当前类的父类的所有属性
        Class<?> superclass = cls.getSuperclass();
        fields = superclass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(fieldName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取get方法
     *
     * @param field
     * @return
     */
    public static Method getGetMethod(Field field) {
        String name = field.getName();
        Class<?> declaringClass = field.getDeclaringClass();
        try {
            return declaringClass.getDeclaredMethod("get" + StringUtils.upperFirstLatter(name), null);
        } catch (NoSuchMethodException e) {
            try {
               return declaringClass.getSuperclass().getMethod("get" + StringUtils.upperFirstLatter(name), null);
            } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            }
        }
        throw new BusinessException("在类和其父类中都未找到给定字段的get方法" + declaringClass.getName() + ":" + name);
    }

    /**
     * 获取get方法
     *
     * @param cls
     * @param name
     * @return
     */
    public static Method getGetMethod(Class<?> cls, String name) {
        try {
            return cls.getDeclaredMethod("get" + StringUtils.upperFirstLatter(name), null);
        } catch (NoSuchMethodException e) {
            try {
                return cls.getSuperclass().getDeclaredMethod("get" + StringUtils.upperFirstLatter(name), null);
            } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            }
        }
        throw new BusinessException("在类和其父类中都未找到给定字段的get方法" + cls.getName() + ":" + name);
    }

    /**
     * 获取set方法
     *
     * @param field
     * @return
     */
    public static Method getSetMethod(Field field) {
        String name = field.getName();
        Class<?> declaringClass = field.getDeclaringClass();
        try {
            return declaringClass.getDeclaredMethod("set" + StringUtils.upperFirstLatter(name), field.getType());
        } catch (NoSuchMethodException e) {
            try {
                return declaringClass.getSuperclass().getDeclaredMethod("set" + StringUtils.upperFirstLatter(name),  field.getType());
            } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取set方法
     *
     * @param cls
     * @param name
     * @return
     */
    public static Method getSetMethod(Class<?> cls, String name) {
        try {
            Field field = cls.getDeclaredField(name);
            return cls.getDeclaredMethod("set" + StringUtils.upperFirstLatter(name), field.getType());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * map 转实体
     *
     * @param cls
     * @param entityMap
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws NoSuchFieldException
     * @throws InvocationTargetException
     */
    public static <T> T mapToEntity(Class<?> cls, Map<String, Object> entityMap) {
        Object obj = null;
        try {
            obj = cls.newInstance();
            for(String key: entityMap.keySet()) {
                Field field = cls.getDeclaredField(key);
                Method setMethod = getSetMethod(field);
                setMethod.invoke(obj, ConvertUtils.convertType(entityMap.get(key), setMethod.getParameterTypes()[0]));
            }
            return (T) obj;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取Class
     *
     * @param <T>
     * @return
     */
    public static <T> Field[] getDBFields(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] declaredFields = cls.getDeclaredFields();
        for(Field field: declaredFields) {

            boolean aStatic = Modifier.isStatic(field.getModifiers());
            if(aStatic) continue;

            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation == null) {
                result = Arrays.copyOf(result, result.length + 1);
                result[result.length - 1] = field;
            }
            if(annotation != null && annotation.value() == true) {
                result = Arrays.copyOf(result, result.length + 1);
                result[result.length - 1] = field;
            }
        }
        return result;
    }

    /**
     * 获取到由数据库字段用 , 隔开的组成的字符串
     *
     * @param cls
     * @return
     */
    public static String[] getDBFieldStrArr(Class<?> cls) {
        Field[] fields = ClassUtil.getDBFields(cls);
        String[] list = new String[0];
        for (Field field : fields) {
            list = ArrayUtils.arrayAdd(list, ClassUtil.getColumnName(field));
        }
        return list;
    }


    /**
     * 获取需要进行外字段校验的字段
     *
     * @param cls
     * @return
     */
    public static List<Field> getForeignField(Class<?> cls) {
        List<Field> result = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();

        for(Field field: fields) {
            Foreign foreign = field.getAnnotation(Foreign.class);
            if(foreign != null) {
                result.add(field);
            }
        }
        return result;
    }

    /**
     * 获取需要进行唯一性校验的字段
     *
     * @param cls
     * @return
     */
    public static List<Field> getUniquenessField(Class<?> cls) {
        List<Field> result = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();

        for(Field field: fields) {
            Uniqueness foreign = field.getAnnotation(Uniqueness.class);
            if(foreign != null) {
                result.add(field);
            }
        }
        return result;
    }

    /**
     * 获取主键的值
     *
     * @param <T>
     * @return
     */
    public static <T> T getPrimaryFieldValue(Object obj) {
        Class<?> cls = obj.getClass();
        Method getPrimaryMethod = getPrimaryGetMethod(cls);
        Object value = null;
        try {
            value = getPrimaryMethod.invoke(obj, null);
            return (T) value;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 获取指定字段的值
     *
     * @param object
     * @param field
     * @return
     */
    public static Object getFieldValue(Object object, String field) {
        Class<?> aClass = object.getClass();
        try {
            Method getMethod = getGetMethod(aClass, field);
            return getMethod.invoke(object, null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * valueInFields
     *
     * @param cls
     * @return
     */
    public static List<Field> getValueInFields(Class<?> cls) {
        List<Field> result = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();

        for(Field field: fields) {
            ValueIn foreign = field.getAnnotation(ValueIn.class);
            if(foreign != null) {
                result.add(field);
            }
        }
        return result;
    }

    /**
     * valueInFields
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFields(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * valueInFields
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldsOneFieldIn(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.type().equals(FillType.ONE_FIELD_IN)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * valueInFields
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldsOneToOne(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.type().equals(FillType.ONE_TO_ONE)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * valueInFields
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldsOneToMultiple(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.type().equals(FillType.ONE_TO_MULTIPLE)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * 获取默认的存在的
     *
     * @param cls
     * @return
     */
    public static List<String> getFillFieldColumnList(Class<?> cls) {
        List<String> result = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.defaultQueryIt() && foreign.type().equals(FillType.ONE_FIELD_IN)) {
                result.add(BaseEntity.humpLine(field.getName()));
            }
        }
        return result;
    }
    /**
     * valueInFields
     *
     * @param cls
     * @return
     */
    public static String[] getFillFieldColumnArr(Class<?> cls) {
        List<String> fillFieldColumnList = getFillFieldColumnList(cls);
        return fillFieldColumnList.toArray(new String[fillFieldColumnList.size()]);
    }

    /**
     * 获取实体注解中的schema
     *
     * @param cls
     * @return
     */
    public static String getSchema(Class<?> cls) {
        DbInfo annotation = cls.getAnnotation(DbInfo.class);
        if(annotation == null) return null;
        return annotation.schema();
    }

    /**
     * 获取实体注解中的schema
     *
     * @param cls
     * @return
     */
    public static Dialect getDialect(Class<?> cls) {
        DbInfo annotation = cls.getAnnotation(DbInfo.class);
        if(annotation == null) return null;
        return annotation.dialect();
    }

    /**
     * outName + "." + outField + " " + BaseEntity.humpLine(field)
     * @param cls
     * @return
     */
    public static String[] getColumnsSQL(Class<?> cls) {
        String simpleName = cls.getSimpleName();
        String[] result = new String[0];
        Field[] declaredFields = cls.getDeclaredFields();
        for(Field field: declaredFields) {
            boolean aStatic = Modifier.isStatic(field.getModifiers());
            if(aStatic) continue;
            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation == null) {
                result = Arrays.copyOf(result, result.length + 1);
                result[result.length - 1] = simpleName + "." + ClassUtil.humpToLine(field.getName()) + " " + simpleName + BaseEntity.humpLine(field);
            }
            if(annotation != null && annotation.value() == true) {
                result = Arrays.copyOf(result, result.length + 1);
                result[result.length - 1] =  simpleName + "." + ClassUtil.humpToLine(field.getName()) + " " + simpleName +  BaseEntity.humpLine(field);
            }
        }
        return result;
    }

    /**
     * 获取默认需要查询出来的字段
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldDefaultQuery(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.defaultQueryIt()) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * 获取默认需要查询出来的单字段填充字段
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldOneFieldInDefaultQuery(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.defaultQueryIt() && foreign.type().equals(FillType.ONE_FIELD_IN)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * 获取默认需要查询出来的单字段填充字段
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldOneFieldIn(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.type().equals(FillType.ONE_FIELD_IN)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * 获取默认需要查询出来的单字段填充字段
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldOneToOneDefaultQuery(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.defaultQueryIt() && foreign.type().equals(FillType.ONE_TO_ONE)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * 获取默认需要查询出来的单字段填充字段
     *
     * @param cls
     * @return
     */
    public static Field[] getFillFieldOneToMulDefaultQuery(Class<?> cls) {
        Field[] result = new Field[0];
        Field[] fields = cls.getDeclaredFields();
        for(Field field: fields) {
            FillField foreign = field.getAnnotation(FillField.class);
            if(foreign != null && foreign.defaultQueryIt() && foreign.type().equals(FillType.ONE_TO_MULTIPLE)) {
                result = ArrayUtils.arrayAdd(result, field);
            }
        }
        return result;
    }

    /**
     * 获取当前方法的名字
     * className + methodName + lineNumber
     *
     * @return
     */
    public static String currentMethod() {
        String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
        String className = Thread.currentThread().getStackTrace()[2].getClassName();
        int lineNumber = Thread.currentThread().getStackTrace()[3].getLineNumber();
        return className + "-" + methodName + "-" + lineNumber;
    }

    /**
     * 数据源组
     *
     * @param cls
     * @return
     */
    public static String getSourceGroup(Class<?> cls) {
        DbInfo annotation = cls.getAnnotation(DbInfo.class);
        if(annotation == null) return null;
        return annotation.sourceGroup();
    }

    /**
     * 获取excelEntity
     *
     * @param cls
     * @return
     */
    public static Class<?> getExcelEntity(Class<?> cls) {
        DbInfo annotation = cls.getAnnotation(DbInfo.class);
        Class<?> tableName;
        if (annotation != null) {
            return annotation.excelEntity();
        }
        return null;
    }
}
